﻿using System;
using System.Linq;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.IO;
using DARE.Effects.RenderEffect;

namespace DARE
{
    public class CMesh
    {
        public enum EHighlightState { NONE, IN, END, OUT };

        #region Fields

        private CModel m_model;

        private readonly ModelMesh m_mesh;
        private readonly CMeshPart[] m_part;

        internal readonly int m_nodeIndex = -1;

        internal Matrix m_relative;
        internal CMesh m_parent;

        private CEffect m_effect = null;


        private float m_highlightSpeedIn;
        private float m_highlightSpeedOut;
        private float m_highlightValue;
        private float m_startEmissiveIntensity;
        private Color m_startEmissiveColor;
        private EHighlightState m_highlightState = EHighlightState.NONE;
        private bool m_highlightCalled;

        #endregion

        #region Property

        public ModelMesh Mesh
        {
            get { return this.m_mesh; }
        }

        public bool IsSkinned
        {
            get;
            private set;
        }

        public int MeshPartCount { get; private set; }

        public DepthStencilState DepthStencilState
        {
            set
            {
                foreach (CMeshPart part in m_part)
                    part.DepthStencilState = value;
            }
        }

        public float HighlightValue
        {
            get { return m_highlightValue; }
            set { m_highlightValue = value; }
        }

        /// <summary>
        /// Get the fading speed is the average of fade in and fade out speeds.
        /// Default value is 1.
        /// </summary>
        public float HighlightSpeed
        {
            get { return (m_highlightSpeedIn + m_highlightSpeedOut) / 2; }
            set
            {
                if (value >= 0)
                {
                    m_highlightSpeedIn = value;
                    m_highlightSpeedOut = value;
                }
            }
        }

        public float HighlightSpeedIn
        {
            get { return m_highlightSpeedIn; }
            set
            {
                if (value >= 0)
                    m_highlightSpeedIn = value;
            }
        }

        public float HighlightSpeedOut
        {
            get { return m_highlightSpeedOut; }
            set
            {
                if (value >= 0)
                    m_highlightSpeedOut = value;
            }
        }

        ///// <summary>
        ///// Property used for serialization and loading
        ///// </summary>
        //[ContentSerializer]
        //internal CRasterizationContent State
        //{
        //    get; { }// return this.m_model.State; }
        //    private set { this.m_model.State = value; }
        //}

        ///// <summary>
        ///// Property of Model's RasterizerState 
        ///// </summary>
        //[ContentSerializerIgnore]
        //public RasterizerState RasterizerState
        //{
        //    get { return this.m_model.RasterizerState; }
        //    set { this.m_model.RasterizerState = value; }
        //}

        public CEffect Effect
        {
            get { return this.m_effect; }
            set
            {
                if (this.m_effect != value)
                    this.m_effect = value;
            }
        }

        public string Name
        {
            get { return this.m_model.Name + "-" + this.RealName; }
            private set
            {
                if (this.RealName != value)
                    this.RealName = value;
            }
        }

        public string RealName { get; private set; }

        public Matrix MeshWorld
        {
            get
            {
                if (this.m_model.AnimationController != null && this.IsSkinned)
                    return Matrix.Identity;
                else if (this.m_model.AnimationController != null)
                    return this.m_relative;
                return this.m_model.ModelWorld;
            }
        }

        public CMeshPart[] Parts
        {
            get { return this.m_part; }
        }

        internal PrimitiveType PrimitiveType { get; private set; }

        internal CModel Model
        {
            get { return this.m_model; }
        }

        public bool Drawable { get; set; }

        public CMeshPart this[int id]
        {
            get
            {
                if (id <= -1 || id >= this.m_part.Length)
                    return null;
                return this.m_part[id];
            }
        }

        #endregion

        #region CTOR

        public CMesh(CModel _model, ModelMesh _mesh, Matrix parentAbsolue)
        {
            Debug.Assert(this.m_mesh == null);

            this.m_mesh = _mesh;
            this.m_model = _model;

            this.m_relative = parentAbsolue;

            MeshPartCount = this.m_mesh.MeshParts.Count;
            this.m_part = new CMeshPart[MeshPartCount];
            for (int i = 0; i < MeshPartCount; ++i)
            {
                this.m_part[i] = new CMeshPart(this.m_mesh.MeshParts[i], this, i);
                if (this.m_part[i].MeshPart.Tag != null)
                (this.m_part[i].MeshPart.Tag as CEffectTextures).LoadPath = Path.GetDirectoryName(Model.Name);
                this.m_part[i].InitializeEffect();
            }

            Name = _mesh.Name;
            Drawable = true;

            if (this.m_model.AsAnimation)
            {
                this.IsSkinned = (this.m_mesh.Tag != null && this.m_mesh.Tag is bool && (bool)this.m_mesh.Tag);
                if (this.IsSkinned)
                {
                    foreach (var elem in m_part)
                    {
                        if (elem.Effect != null)
                            elem.Effect.IsAnimationActive = true;
                    }
                    this.Effect = new CEffect(this.m_model.Effect);
                    this.Effect.IsAnimationActive = true;
                }
                CAnimNode[] _anims = this.m_model.AnimationController.m_hierarchy.Nodes;
                for (int j = 0; j < _anims.Length; ++j)
                {
                    if (_anims[j].Name == Mesh.Name)
                    {
                        this.m_nodeIndex = j;
                        break;
                    }
                }
            }
            HighlightValue = 0.5f;
            HighlightSpeed = 1;
                
        }

        #endregion

        #region Event
        #endregion

        #region Method

        //internal void InitializePart()
        //{
        //    for (int i = 0; i < this.m_part.Length; ++i)
        //    {
        //        if (this.m_part[i].MeshPart.Tag != null)
        //             (this.m_part[i].MeshPart.Tag as CEffectTextures).LoadPath = Path.GetDirectoryName(Model.Name);
        //    }
        //}

        //internal void Unsubscribe()
        //{
        //    //for (int i = 0; i < this.MeshPartCount; ++i)
        //    //{
        //    //    this.m_model.Renderer.UnRegisterOrdererLeaf(this.Parts[i]);
        //    //}
        //}

        //private void DataUpdated()
        //{
        //    for (int i = 0; i < this.MeshPartCount; ++i)
        //        this.Parts[i].DataChanged();
        //}

        internal void UpdateRelative(CAnimationController _cac)
        {
            this.m_relative = _cac.WorldTransform[this.m_nodeIndex];
        }


        public void Highlight()
        {
            if (m_highlightState == EHighlightState.NONE)
            {
                m_startEmissiveIntensity = Effect.EmissiveLightIntensity;
                m_startEmissiveColor = Effect.EmissiveLightColor;
                m_highlightState = EHighlightState.IN;
            }

        }

        public void UpdateHighlight(GameTime gameTime)
        {
            float time = CDare.Instance.Config.Time.FixedTimeSinceLastFrame / 1000.0f;

            switch (m_highlightState)
            {
                case EHighlightState.NONE:
                    break;

                case EHighlightState.IN:
                    Effect.EmissiveLightIntensity += m_highlightSpeedIn * time;

                    if (Effect.EmissiveLightIntensity > m_highlightValue)
                    {
                        Effect.EmissiveLightIntensity = m_highlightValue;
                        m_highlightState = EHighlightState.OUT;
                    }
                    Effect.EmissiveLightColor = Color.Lerp(m_startEmissiveColor, Color.White, Effect.EmissiveLightIntensity / m_highlightValue);
                    break;

                case EHighlightState.END:
                    break;

                case EHighlightState.OUT:
                    Effect.EmissiveLightIntensity -= m_highlightSpeedOut * time;

                    if (Effect.EmissiveLightIntensity < m_startEmissiveIntensity)
                    {
                        Effect.EmissiveLightIntensity = m_startEmissiveIntensity;
                        m_highlightState = EHighlightState.NONE;
                    }
                    Effect.EmissiveLightColor = Color.Lerp(Color.White, m_startEmissiveColor, Effect.EmissiveLightIntensity / m_highlightValue);
                    break;
            }
            m_highlightCalled = false;
        }

        #endregion

    }
}
